package _effx.greensock
{
   import com.greensock.TweenLite;
   import com.greensock.plugins.TweenPlugin;
   
   import flash.display.Bitmap;
   import flash.display.BitmapData;
   import flash.display.DisplayObject;
   import flash.events.Event;
   import flash.geom.Matrix;
   import flash.geom.Rectangle;
   import flash.utils.Dictionary;

   /**
    * @author created by milkmidi, maintained by cjboy1984@gmail.com
    * @see origin - http://milkmidi.blogspot.com
    * @version 1.1
    * @update 2011/01/14
    * @usage
    *
    * // Mostly set blendMode = BlendMode.LAYER
    * blendMode = BlendMode.LAYER;
    * TweenPlugin.activate([MosaicPlugin]);
    *
    * TweenMax.to(mc, .5, { mosaic:{start:0, end:5, autoDestroy:false } } );
    * TweenMax.to(mc, .5, { mosaic:{start:5, end:0} } );
    */
   public class MosaicPlugin extends TweenPlugin
   {
      private const BMP_NAME:String = '_gs_mosaic_';

      public static const VERSION:Number = 1.0;
      public static const API:Number = 1.0;

      private var target:DisplayObject;
      private var targetClone:Bitmap;
      private var targetBound:Rectangle;
      private var targetParentBound:Rectangle;
      private var startValue:int = 0;
      private var endValue:int = 20;
      private var _autoDestroy:Boolean = true;

      // dictionaries
      private static var _nameDict:Dictionary = new Dictionary();
      private static var _renderFuncDict:Dictionary = new Dictionary();

      public function MosaicPlugin()
      {
         super();
         this.propName = "mosaic";
         this.overwriteProps = ["mosaic"];
         this.onComplete = onCompleteTween;
      }

      // --------------------- LINE ---------------------

      override public function onInitTween($target:Object, pValue:*, tween:TweenLite):Boolean
      {
         if (!($target is DisplayObject)) return false;
         if (typeof(pValue) != "object") return false;

         target = DisplayObject($target);
         if (!target.parent) return false; // no parent

         // find name from dictionary.
         var nname:String;
         if (_nameDict[target] == undefined)
         {
            nname = BMP_NAME + target.name;
            _nameDict[target] = nname;
         }
         else
         {
            nname = _nameDict[target] as String;
         }

         // find bmp from name.
         targetClone = target.parent.getChildByName(nname) as Bitmap;
         if (targetClone == null)
         {
            targetClone = new Bitmap();
            targetClone.name = nname;
         }
         var targetIndex:int = target.parent.getChildIndex(target);
         target.parent.addChildAt(targetClone, targetIndex);
         target.visible = false;

         startValue = pValue.start;
         endValue = pValue.end;
         _autoDestroy = (pValue.autoDestroy == undefined) ? true : pValue.autoDestroy ;

         targetBound = target.getBounds(target);
         targetParentBound = target.getBounds(target.parent);

         // remove effect if target is removed from stage.
         target.addEventListener(Event.REMOVED_FROM_STAGE, onRemove);

         return true;
      }

      // --------------------- LINE ---------------------

      override public function killProps($lookup:Object):void
      {
         super.killProps($lookup);
         _autoDestroy = true;
         onCompleteTween();
      }

      // --------------------- LINE ---------------------

      override public function get changeFactor():Number { return super.changeFactor; }
      override public function set changeFactor(value:Number):void
      {
         super.changeFactor = value;

         var pxSize:Number;
         if (startValue < endValue)
         {
            pxSize = (value * endValue) + 1 + startValue;
         }
         else
         {
            pxSize = ((1 - value) * startValue) + 1 + endValue;
         }

         // get boundary rectangle relative to the coordinate system of target.parent
         targetParentBound = target.getBounds(target.parent);

         // Buffering the image after reducing the original image to pxSize, then buffering back to orignal.
         var _matrix:Matrix = new Matrix();
         _matrix.scale(1 / pxSize, 1 / pxSize);
         _matrix.translate( -targetBound.x / pxSize, -targetBound.y / pxSize); // 2010/01/04

         targetClone.bitmapData = new BitmapData(targetBound.width / pxSize, targetBound.height / pxSize, true, 0x00000000);
         targetClone.bitmapData.draw(target, _matrix);

         // TODO - Set all the proporties of target to targetClone, including filters, x, y, alpha, etc.
         targetClone.alpha = target.alpha;
         targetClone.x = targetParentBound.x; // 2010/01/04
         targetClone.y = targetParentBound.y; // 2010/01/04
         targetClone.width = targetBound.width;
         targetClone.height = targetBound.height;
      }

      // ################### protected ##################

      // #################### private ###################

      private function onCompleteTween():void
      {
         if (_autoDestroy == false) return;

         target.visible = true;

         // name dictionary
         delete _nameDict[target];

         if (target.parent && target.parent.contains(targetClone))
         {
            target.parent.removeChild(targetClone);
         }

         if (targetClone.bitmapData)
         {
            targetClone.bitmapData.dispose();
         }

         targetClone = null;
      }

      // --------------------- LINE ---------------------

      /**
       * Prevent the target from being removed from stage without killing its tween.
       */
      private function onRemove(e:Event):void
      {
         target.removeEventListener(Event.REMOVED_FROM_STAGE, onRemove);

         _autoDestroy = true;
         onCompleteTween();
      }

      // --------------------- LINE ---------------------

   }

}